
#ifndef __RSP_CMDBUF__HEADER_FILE__
#define __RSP_CMDBUF__HEADER_FILE__


#include "coid/comm/binstream/circularstreambuf.h"

////////////////////////////////////////////////////////////////////////////////
class rsp_commandbuffer : public circularstreambuf
{
protected:
public:
    rsp_commandbuffer( uints size = 0 ) : circularstreambuf(size)
    {
    }

    void reset();

    uints bytes_to_read_pck0() const        { return _endpck==_begpck ? 0 : _lens[_begpck]; }


    ///Set packet to read
    ///@note this hacks the circularstreambuf somewhat to serve different packet than
    /// the first one when reading
    bool prepare_packet_to_read( uint pck )
    {
        check_packet(pck);

        pck += _begpck;
        if( pck >= _offs.size() )
            pck -= _offs.size();

        _sizerd = _lens[pck];
        _ptrrd = _buf.ptr() + _offs[pck];
        return _sizerd > 0;
    }

    ///@return true if empty packet 
    bool prepare_packet_to_write( uint pck, bool append )
    {
        uint npck = _endpck - _begpck;
        if( _endpck < _begpck )
            npck += _offs.size();

        if( pck > npck ) {
            for( ; pck>npck; ++npck )
                flush();
            return true;
        }
        else if( pck < npck ) {
/*
            for( ; pck < npck; ++pck ) {
                --_endpck;
                if( _endpck == UMAX )
                    _endpck = _offs.size()-1;
                _size -= _lens[_endpck];
            }
*/
            uints s = _offs[_endpck];
            _endpck = _begpck + pck;
            if( _endpck >= _offs.size() )
                _endpck -= _offs.size();
            s -= _offs[_endpck];
            if( s >= _buf.size() )
                s += _buf.size();
            _size -= s;

            _sizewr = append ? _lens[_endpck] : 0;
        }

        return _sizewr == 0;
    }

    uints anything_to_read_pck0()
    {
        if( _endpck == _begpck )  {
            _sizerd = 0;
            return 0;
        }
        _sizerd = _lens[_begpck];
        if( _sizerd == 0 )
            return 0;

        _ptrrd = _buf.ptr() + _offs[_begpck];
        return _sizerd;
    }

    bool anything_to_read( uint pck )
    {
        uint npck = _endpck - _begpck;
        if( _endpck < _begpck )
            npck += _offs.size();

        if( pck > npck )  return false;
        if( pck == npck )
        {
            if( _sizewr == 0 )  return false;
            flush();
        }

        pck += _begpck;
        if( pck >= _offs.size() )
            pck -= _offs.size();

        if( _lens[pck]==0 )
            return false;

        _sizerd = _lens[pck];
        _ptrrd = _buf.ptr() + _offs[pck];

        return true;
    }

    opcd write_packet( uints size, binstream& src )
    {
        if(size)
        {
            pckdata a,b;
            get_space( size, a, b );

            //uint as=a.size, bs=b.size;

            opcd e = src.read_raw_full( a.ptr, a.size );
            if(e)  return e;
            if( b.size )
                e = src.read_raw_full( b.ptr, b.size );
            if(e) return e;

            //DASSERT( cdcd_memcheck( a.ptr, a.ptr+as, b.ptr, b.ptr+bs ) );
        }

        //flush();
        return 0;
    }

    opcd read_packet( uint pck, binstream& dst )
    {
        pckdata a,b;
        get_packet( pck, a, b );

        //DASSERT( cdcd_memcheck( a.ptr, a.ptr+a.size, b.ptr, b.ptr+b.size ) );

        opcd e = dst.write_raw( a.ptr, a.size );
        if(e)  return e;
        if( b.size )
            e = dst.write_raw( b.ptr, b.size );

        //if(!e) acknowledge();
        return e;
    }

    bool compare_read_packets( const rsp_commandbuffer& cb ) const
    {
        if( _lens[_begpck] != cb._lens[cb._begpck] )  return false;

        uints s = _lens[_begpck];
        if(!s) return true;
        const uchar* p = _ptrrd;
        const uchar* d = cb._ptrrd;
        for( ; s>0; --s)
        {
            if( *p != *d )  return false;
            ++p;
            ++d;
            if( p >= _buf.ptre() )      p=_buf.ptr();
            if( d >= cb._buf.ptre() )   d=cb._buf.ptr();
        }
        return true;
    }

    uint get_packet_length( uint pck  ) const
    {
        uint npck = _endpck-_begpck;
        if( _endpck < _begpck )
            npck += _offs.size();
        if( pck >= npck )  return 0;

        pck += _begpck;
        if( pck >= _offs.size() )
            pck -= _offs.size();
        return _lens[pck];
    }


    ///Get lengths of subsequent packets from position \a pck
    ///@return number of leading empty packets
    uint get_packet_lengths( uint pck, uint n, dynarray<ushort>& lens, uint* totlen ) const
    {
        uint npck = _endpck-_begpck;
        if( _endpck < _begpck )
            npck += _offs.size();
        DASSERT( pck < npck );
        if( n == UMAX  ||  pck+n > npck )
            n = npck - pck;

        uint m = (uint)_offs.size();
        uint p = _begpck+pck;
        lens.need(n);
        uint tlen = 0;
        uint of = 0;
        uint last = UMAX;
        for( uint i=0; i<n; ++i,++p )
        {
            if( p >= m )  p -= m;
            lens[i] = _lens[p];
            if(lens[i])
                last = i;
            tlen += lens[i];
            if(!tlen)
                ++of;
        }

        if( ++last < lens.size() )
        {
            if( of >= last )  return n;
            lens.need(last);
        }

        if(totlen) *totlen = tlen;
        return of;
    }

    void client_reset()
    {
        _size = 0;
        _sizewr = _sizerd = 0;
        
        _begpck = _endpck = 0;
        _offs[0] = 0;
    }

protected:
    void check_packet( uint pck )
    {
        uint npck = _endpck - _begpck;
        if( _endpck < _begpck )
            npck += _offs.size();

        for( ; npck<=pck; ++npck )
            flush();
    }
};

#endif //__RSP_CMDBUF__HEADER_FILE__
